package com.prd.service.Impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.prd.config.LoginInterceptor;
import com.prd.dto.SysMenuDTO;
import com.prd.enums.SysIsDeleteTyPeEnum;
import com.prd.mapper.SysRoleMenuMapper;
import com.prd.model.LoginUser;
import com.prd.model.SysMenuDO;
import com.prd.mapper.SysMenuMapper;
import com.prd.model.SysRoleMenuDO;
import com.prd.service.SysMenuService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.prd.util.CommonUtil;
import com.prd.util.JsonData;
import com.prd.util.StringUtils;
import com.prd.vo.RouterVo;
import com.prd.vo.TreeSelect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import springfox.documentation.spring.web.json.Json;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 菜单（权限）数据表 服务实现类
 * </p>
 *
 * @author lizhaoyi
 * @since 2024-01-09
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenuDO> implements SysMenuService {

    @Autowired
    private SysMenuMapper sysMenuMapper;

    @Autowired
    private SysRoleMenuMapper roleMenuMapper;

    /**
     * 获取菜单列表
     * @param sysMenuDTO
     * @return
     */
    @Override
    public JsonData selectMenuList(SysMenuDTO sysMenuDTO) {

        LoginUser loginUser = LoginInterceptor.threadLocal.get();

        if (loginUser == null) {
            return JsonData.buildError("用户未登录");
        }

        List<SysMenuDO> menuDOList = getMenuList(sysMenuDTO, loginUser.getId());

        if (!CollectionUtils.isEmpty(menuDOList)) {
            List<SysMenuDTO> sysMenuDTOS = CommonUtil.dtoListTransfer(menuDOList, SysMenuDTO.class);
            return JsonData.buildSuccess(sysMenuDTOS);
        }

        return JsonData.buildSuccess(menuDOList);
    }

    /**
     * 根据用户查询系统菜单列表
     * @param userId
     * @return
     */
    @Override
    public JsonData selectMenuList(Long userId) {
        return JsonData.buildSuccess(CommonUtil.dtoListTransfer(getMenuList(new SysMenuDTO(),userId),SysMenuDTO.class));
    }

    /**
     * 根据菜单ID查询信息
     * @param menuId
     * @return
     */
    @Override
    public JsonData selectMenuById(Long menuId) {
        return JsonData.buildSuccess(sysMenuMapper.selectById(menuId));
    }

    /**
     *  获取菜单下拉树列表
     * @param sysMenuDTO
     * @return
     */
    @Override
    public JsonData treeSelect(SysMenuDTO sysMenuDTO) {

        Long userId = LoginInterceptor.threadLocal.get().getId();
        //获取下拉菜单列表
        List<SysMenuDTO> sysMenuDTOS = CommonUtil.dtoListTransfer(getMenuList(sysMenuDTO, userId), SysMenuDTO.class);

        return JsonData.buildSuccess(buildMenuTree(sysMenuDTOS));
    }

    /**
     * 加载对应角色菜单列表树
     * @param roleId
     * @return
     */
    @Override
    public JsonData selectMenuListByRoleId(Long roleId) {

        //获取下拉菜单列表
        List<SysMenuDO> menuDOList = sysMenuMapper.selectMenuListByRoleId(roleId);

        List<SysMenuDTO> sysMenuDTOS = CommonUtil.dtoListTransfer(menuDOList, SysMenuDTO.class);

        return JsonData.buildSuccess(buildMenuTree(sysMenuDTOS));
    }

    private List<TreeSelect> buildMenuTree(List<SysMenuDTO> sysMenuDTOS) {

        List<SysMenuDTO> returnList = new ArrayList<>();
        List<Long> menuIds = sysMenuDTOS.stream().map(SysMenuDTO::getId).collect(Collectors.toList());
        //构建下拉树
        for (SysMenuDTO dto : sysMenuDTOS) {
            if (!menuIds.contains(dto.getParentId())) {
                recursionFn(sysMenuDTOS,dto);
                returnList.add(dto);
            }
        }
        if (returnList .isEmpty()) {

            returnList = sysMenuDTOS;
        }

        return returnList.stream().map(TreeSelect::new).collect(Collectors.toList());
    }

    /**
     * 新增菜单
     * @param sysMenuDTO
     * @return
     */
    @Override
    public JsonData insertMenu(SysMenuDTO sysMenuDTO) {
        if (!checkMenuNameUnique(sysMenuDTO)) {
            return JsonData.buildError("新增菜单"+sysMenuDTO.getName()+"失败，菜单名称已存在");
        }
        LoginUser loginUser = LoginInterceptor.threadLocal.get();
        if (loginUser == null) {
            sysMenuDTO.setCreator("system");
        }else {
            sysMenuDTO.setCreator(loginUser.getName());
        }
        sysMenuDTO.setCreateTime(LocalDateTime.now());
        sysMenuDTO.setDeleted(SysIsDeleteTyPeEnum.UN_DELETE.getCode());
        sysMenuDTO.setVisible(0);
        sysMenuMapper.insert(CommonUtil.dtoTransfer(sysMenuDTO,SysMenuDO.class));

        return JsonData.buildSuccess();
    }


    /**
     * 修改菜单
     * @param sysMenuDTO
     * @return
     */
    @Override
    public JsonData updateMenu(SysMenuDTO sysMenuDTO) {

        if (!checkMenuNameUnique(sysMenuDTO)) {
            return JsonData.buildError("修改菜单"+sysMenuDTO.getName()+"失败，菜单名称已存在");
        }
        if (sysMenuDTO.getId().equals(sysMenuDTO.getParentId())) {
            return JsonData.buildError("修改菜单"+sysMenuDTO.getName()+"失败，上级菜单不能选择自己");
        }

        sysMenuMapper.updateById(CommonUtil.dtoTransfer(sysMenuDTO,SysMenuDO.class));

        return JsonData.buildSuccess();
    }

    /**
     * 删除菜单
     * @param menuId
     * @return
     */
    @Override
    public JsonData deleteMenuById(Long menuId) {

        if (hasChildByMenuId(menuId)) {
            return JsonData.buildError("存在子菜单,不允许删除");
        }
        if (checkMenuExistRole(menuId)) {
            return JsonData.buildError("菜单已分配,不允许删除");
        }

        sysMenuMapper.deleteById(menuId);

        return JsonData.buildSuccess();
    }


    /**
     * 查询菜单是否存在角色
     * @param menuId
     * @return
     */
    private boolean checkMenuExistRole(Long menuId) {
        Integer count = roleMenuMapper.selectCount(new LambdaQueryWrapper<SysRoleMenuDO>().eq(SysRoleMenuDO::getMenuId, menuId));
        return count > 0;
    }

    /**
     * 是否存在菜单子节点
     * @param menuId
     * @return
     */
    private boolean hasChildByMenuId(Long menuId) {
        Integer count = sysMenuMapper.selectCount(new LambdaQueryWrapper<SysMenuDO>().eq(SysMenuDO::getParentId, menuId));
        return count > 0;
    }


    /**
     * 获取菜单列表
     * @param sysMenuDTO
     * @param userId
     * @return
     */
    private List<SysMenuDO> getMenuList(SysMenuDTO sysMenuDTO,Long userId) {

        List<SysMenuDO> menuDOList = null;

        //管理员显示所有菜单
        if (SysMenuDTO.isAdmin(userId)) {

            menuDOList = sysMenuMapper.selectMenuList(sysMenuDTO);

        }else {

            menuDOList = sysMenuMapper.selectMenuListByUserId(sysMenuDTO, userId);

        }
        return menuDOList;
    }

    /**
     * 递归列表
     * @param sysMenuDTOList
     * @param sysMenuDTO
     */
    private void recursionFn(List<SysMenuDTO> sysMenuDTOList, SysMenuDTO sysMenuDTO)
    {
        // 得到子节点列表
        List<SysMenuDTO> childList = getChildList(sysMenuDTOList, sysMenuDTO);
        sysMenuDTO.setChildren(childList);
        for (SysMenuDTO tChild : childList) {
            if (hasChild(sysMenuDTOList, tChild)) {

                recursionFn(sysMenuDTOList, tChild);
            }
        }
    }

    /**
     * 得到子节点列表
     */
    private List<SysMenuDTO> getChildList(List<SysMenuDTO> sysMenuDTOList, SysMenuDTO sysMenuDTO) {

        List<SysMenuDTO> childList = new ArrayList<>();

        for (SysMenuDTO dto : sysMenuDTOList) {
            if (dto.isChildMenu(sysMenuDTO.getId())){
                childList.add(dto);
            }
        }

        return childList;
    }

    /**
     * 判断是否有子节点
     */
    private boolean hasChild(List<SysMenuDTO> sysMenuDTOList, SysMenuDTO sysMenuDTO) {

        return getChildList(sysMenuDTOList, sysMenuDTO).size() > 0;
    }

    /**
     * 校验菜单名称是否唯一
     * @param sysMenuDTO
     * @return
     */
    private boolean checkMenuNameUnique(SysMenuDTO sysMenuDTO) {

        SysMenuDO sysMenuDO = sysMenuMapper.selectOne(new LambdaQueryWrapper<SysMenuDO>().eq(SysMenuDO::getName, sysMenuDTO.getName()));
        if (sysMenuDTO.getParentId() != null) {
            if (sysMenuDO != null && sysMenuDO.getParentId().equals(sysMenuDTO.getParentId())) {
                return false;
            }
        }else {
            if (sysMenuDO != null) {
                return false;
            }
        }

        return true;
    }
}
